﻿using System;
using System.Collections.Generic;

namespace IInject.Reflection
{
	public static class ReflectionProvider
	{
		#region Constructors

		static ReflectionProvider()
		{
		}

		#endregion

		#region Methods

		public static T CreateInstance<T>()
		{
			return Activator.CreateInstance<T>();
		}

		public static T CreateInstance<T>(params object[] paramValues)
		{
			object value = CreateInstance(typeof(T), paramValues);

			if (Inherits<T>(value))
			{
				return (T)value;
			}
			
			return default(T);
		}

		public static object CreateInstance(Type type)
		{
			return Activator.CreateInstance(type);
		}

		public static object CreateInstance(Type type, params object[] paramValues)
		{
			return Activator.CreateInstance(type, paramValues);
		}

		public static T CreateInstance<T>(Constructor constructor)
		{
			object value = constructor.Invoke();

			if (Inherits<T>(value))
			{
				return (T)value;
			}

			return default(T);
		}

		public static object CreateInstance(Constructor constructor)
		{
			return constructor.Invoke();
		}

		public static T CreateInstance<T>(Constructor constructor, params object[] paramValues)
		{
			object value = constructor.Invoke(paramValues);

			if (Inherits<T>(value))
			{
				return (T)value;
			}

			return default(T);
		}

		public static object CreateInstance(Constructor constructor, params object[] paramValues)
		{
			return constructor.Invoke(paramValues);
		}

		public static Constructor GetConstructor<T>(params Type[] paramTypes)
		{
			return GetConstructor(typeof(T), paramTypes);
		}

		public static Constructor GetConstructor(Type type, params Type[] paramTypes)
		{
			return new Constructor(type.GetConstructor(paramTypes));
		}

		public static Method GetMethod<T>(string method, params Type[] paramTypes)
		{
			return GetMethod(typeof(T), method, paramTypes);
		}

		public static Method GetMethod(Type type, string method, params Type[] paramTypes)
		{
			return new Method(type.GetMethod(method, paramTypes));
		}

		public static Method GetPropertyGetMethod<T>(string property)
		{
			return GetPropertyGetMethod(typeof(T), property);
		}

		public static Method GetPropertyGetMethod(Type type, string property)
		{
			return GetProperty(type, property).GetGetMethod();
		}

		public static Method GetPropertySetMethod<T>(string property)
		{
			return GetPropertySetMethod(typeof(T), property);
		}

		public static Method GetPropertySetMethod(Type type, string property)
		{
			return GetProperty(type, property).GetSetMethod();
		}

		public static IEnumerable<Property> GetProperties<T>()
		{
			var results = new List<Property>();

			foreach (var property in typeof(T).GetProperties())
			{
				results.Add(new Property(property));
			}

			return results;
		}

		public static Property GetProperty<T>(string property)
		{
			return GetProperty(typeof(T), property);
		}

		public static Property GetProperty(Type type, string property)
		{
			return new Property(type.GetProperty(property));
		}

		public static T GetPropertyValue<T>(Property property, object component)
		{
			object value = GetPropertyValue(property, component);

			if (Inherits<T>(value))
			{
				return (T)value;
			}

			return default(T);
		}

		public static object GetPropertyValue(Property property, object component)
		{
			return property.GetValue(component);
		}

		public static void SetPropertyValue(Property property, object component, object value)
		{
			property.SetValue(component, value);
		}

		private static bool Inherits<T>(object value)
		{
			return value != null && value.GetType().IsAssignableFrom(typeof(T));
		}

		#endregion
	}
}